using System;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Security;
using log4net;

namespace Aluetjen.EmailService.Client
{
	/// <summary>
	/// This is an abstraction to the web service client to allow Unit Testing
	/// </summary>
	internal sealed class ServiceClientFactory<T> : ClientBase<T>, IServiceClientFactory<T> where T : class
	{
        private readonly ILog _logger = LogManager.GetLogger(typeof(ServiceClientFactory<T>));

		public ServiceClientFactory()
		{
		}

		public ServiceClientFactory(string endPointConfigurationName)
			: base(endPointConfigurationName)
		{
		}

        public ServiceClientFactory(string endPointConfigurationName, string endPointAddress)
            : base(endPointConfigurationName, endPointAddress)
        {
        }

        public ServiceClientFactory(Binding binding, EndpointAddress endPointAddress)
            : base(binding, endPointAddress)
        {
        }

		#region IServiceClientFactory<T> Members

		public T CreateClient()
		{
			var channel = CreateChannel();

            // Explicitly open the channel to avoid internal synchronisation for auto-opening the channel.
            ((ICommunicationObject)channel).Open();

		    return channel;
		}

		public void DisposeClient(T clientInstance)
		{
			if (clientInstance == null)
			{
                throw new ArgumentNullException("clientInstance");
			}

			if (!(clientInstance is ICommunicationObject))
		    {
			    throw new ArgumentException("Object is not a WCF channel.", "clientInstance");
			}

			var client = (ICommunicationObject)clientInstance;

			try
			{
                // From previous elaborations we experienced that the state of a client might not be reliable.
                // Therefore we always try to close the channel no matter in what state it is.
                client.Close();
			}
			catch (Exception ex)
			{
				_logger.Error("An exception occurred by attempting to 'Close' a Service Client", ex);

			    try
			    {
                    client.Abort();
			    }
			    catch
			    {
			        // Ignore if abort failed.
			    }
			}
		}
		#endregion
	}
}